package utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 *
 * 项目名称：msf-cloudbuilder-service<br>
 * 类名称：FileUtil<br>
 * 类描述：文件类操作<br>
 * 创建人：weixiang.huan<br>
 * 创建时间：2017年5月8日 上午9:22:08<br>
 * 修改人：<br>
 * 修改时间：<br>
 * 修改备注： <br>
 */
public final class FileUtil {
	private static Logger logger = LoggerFactory.getLogger(FileUtil.class);
	private static final int BYTE_SIZE = 5120;
	private static final int COUNT = 1024;
	private static final int INDEX_DIR_TO = 2;
	private static final int INDEX_DIR_TH = 3;

	private FileUtil() {
	}

	// 创建目录
	public static boolean createDir(String destDirName) {
		String createDirName = destDirName;
		if (!destDirName.endsWith(File.separator)) {// 结尾是否以"/"结束
			createDirName = destDirName + File.separator;
		}
		File dir = new File(createDirName);
		if (dir.exists()) {// 判断目录是否存在
			logger.debug("创建目录失败，目标目录已存在！");
			return false;
		}
		if (dir.mkdirs()) {// 创建目标目录
			logger.debug("创建目录成功！" + createDirName);
			return true;
		} else {
			logger.error("创建目录失败！");
			return false;
		}
	}

	// 判断文件目录是否存在
	public static boolean isExit(String destDirName) {
		File dir = new File(destDirName);
		return dir.exists();
	}

	/**
	 * 根据字符串创建文件，并写入数据
	 *
	 * @param sourceString
	 * @param path
	 * @return
	 * @author 陈亚东
	 */
	public static boolean createFile(String sourceString, String path) {
		byte[] sourceByte = sourceString.getBytes();
		if (null != sourceByte) {
			try {
				File file = new File(path); // 文件路径（路径+文件名）
				if (!file.exists()) { // 文件不存在则创建文件，先创建目录
					File dir = new File(file.getParent());
					dir.mkdirs();
					file.createNewFile();
				}
				FileOutputStream outStream = new FileOutputStream(file); // 文件输出流用于将数据写入文件
				outStream.write(sourceByte);
				outStream.close(); // 关闭文件输出流
			} catch (Exception e) {
				logger.error("创建文件！"+e.getMessage());
			}
		}
		return true;
	}

	/**
	 * 根据路径读取文件内容
	 *
	 * @param path
	 * @return
	 * @author 陈亚东
	 */
	public static String readFile(String path) {
		String str = "";
		File file = new File(path);
		try {
			BufferedReader rd = new BufferedReader(new FileReader(file));
			String s = rd.readLine();
			while (null != s) {
				str += s + "\n";
				s = rd.readLine();
			}
			rd.close();
		} catch (FileNotFoundException e) {
			logger.error("根据路径读取文件内容执行异常:" + e.getMessage());
			throw new RuntimeException("根据路径读取文件内容执行异常:" + e.getMessage());
		} catch (IOException e) {
			logger.error("根据路径读取文件内容执行异常:" + e.getMessage());
			throw new RuntimeException("根据路径读取文件内容执行异常:" + e.getMessage());
		}
		return str;
	}

	/**
	 * 递归删除目录下的所有文件及子目录下所有文件
	 *
	 * @param dir
	 *            将要删除的文件目录
	 * @return
	 * @author 陈亚东
	 */
	public static boolean deleteDir(File dir) {
		if (dir.isDirectory()) {
			String[] children = dir.list();
			// 递归删除目录中的子目录下
			for (int i = 0; i < children.length; i++) {
				boolean success = deleteDir(new File(dir, children[i]));
				if (!success) {
					return false;
				}
			}
		}
		// 目录此时为空，可以删除
		return dir.delete();
	}

	public static void deleteFile(File file){
		if(file.isFile()){
			file.delete();
		}else if(file.isDirectory()){
			File[] files = file.listFiles();
			for(int i=0;i<files.length;i++){
				deleteFile(files[i]);
			}
			file.delete();
		}
	}

	/**
	 * 删除文件
	 *
	 * @param path
	 * @author 陈亚东
	 */
	public static void deleteFile(String path) {
		File file = new File(path);
		if (file.exists() && file.isFile()) {
			file.delete();
		}
	}

	// 删除文件夹
	public static void deleteFolder(File file) {
		if (file.list() == null) {
			return;
		}
		if (file.isFile() || file.list().length == 0) {
			file.delete();
		} else {
			File[] files = file.listFiles();
			for (int i = 0; i < files.length; i++) {
				deleteFolder(files[i]);
				try {
					files[i].delete();
				} catch (Exception e) {
					logger.error(e.getMessage());
				}
			}
		}
	}

	/**
	 * 递归压缩文件夹
	 *
	 * @param srcRootDir
	 *            压缩文件夹根目录的子路径
	 * @param file
	 *            当前递归压缩的文件或目录对象
	 * @param zos
	 *            压缩文件存储对象
	 * @throws Exception
	 */
	private static void zip(String srcRootDir, File file, ZipOutputStream zos) throws Exception {
		if (file == null) {
			return;
		}
		logger.debug(srcRootDir);
		// 如果是文件，则直接压缩该文件
		if (file.isFile()) {
			int count;
			int bufferLen = COUNT;
			byte[] data = new byte[bufferLen];

			// 获取文件相对于压缩文件夹根目录的子路径
			String subPath = file.getAbsolutePath();
			logger.debug("文件相对于压缩文件夹根目录的子路径" + subPath);
			int index = subPath.indexOf(srcRootDir);
			if (index != -1) {
				// subPath = subPath.substring(srcRootDir.length() +
				// File.separator.length());
				subPath = subPath.substring(index);
				String[] dir;
				String os = System.getProperty("os.name");
				if (os.toLowerCase().startsWith("win")) {
					dir = subPath.split("\\\\");
				} else {
					dir = subPath.split("/");
				}
				int secondIndex = (dir[0] + dir[1] + dir[INDEX_DIR_TO]).length();
				subPath = subPath.substring(secondIndex + INDEX_DIR_TH);

			}
			logger.debug("文件相对于压缩文件夹根目录的子路径" + subPath);
			ZipEntry entry = new ZipEntry(subPath);
			zos.putNextEntry(entry);
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
			while ((count = bis.read(data, 0, bufferLen)) != -1) {
				zos.write(data, 0, count);
			}
			bis.close();
			zos.closeEntry();
		} else {// 如果是目录，则压缩整个目录
				// 压缩目录中的文件或子目录
			File[] childFileList = file.listFiles();
			for (int n = 0; n < childFileList.length; n++) {
				childFileList[n].getAbsolutePath().indexOf(file.getAbsolutePath());
				zip(srcRootDir, childFileList[n], zos);
			}
		}
	}

	/**
	 * 对文件或文件目录进行压缩
	 *
	 * @param srcPath
	 *            要压缩的源文件路径。如果压缩一个文件，则为该文件的全路径；如果压缩一个目录，则为该目录的顶层目录路径
	 * @param zipPath
	 *            压缩文件保存的路径。注意：zipPath不能是srcPath路径下的子文件夹
	 * @param zipFileName
	 *            压缩文件名
	 * @throws Exception
	 */
	public static void zip(String srcPath, String zipPath, String zipFileName) {
		logger.info("=================开始压缩文件====================");
		if (StringUtils.isEmpty(srcPath) || StringUtils.isEmpty(zipPath) || StringUtils.isEmpty(zipFileName)) {
			throw new RuntimeException("zip包参数输入有错");
		}
		CheckedOutputStream cos = null;
		ZipOutputStream zos = null;
		try {
			logger.debug("要压缩的源文件" + srcPath);
			logger.debug("压缩文件保存的路径" + zipPath);
			File srcFile = new File(srcPath);

			// 判断压缩文件保存的路径是否为源文件路径的子文件夹，如果是，则抛出异常（防止无限递归压缩的发生）
			if (srcFile.isDirectory() && zipPath.indexOf(srcPath) != -1) {
				throw new RuntimeException("zipPath不能是srcPath的子目录");
			}

			// 判断压缩文件保存的路径是否存在，如果不存在，则创建目录
			File zipDir = new File(zipPath);
			if (!zipDir.exists() || !zipDir.isDirectory()) {
				zipDir.mkdirs();
			}

			// 创建压缩文件保存的文件对象
			String zipFilePath = zipPath + zipFileName;
			logger.debug("创建压缩文件保存的文件对象" + zipFilePath);
			File zipFile = new File(zipFilePath);
			if (zipFile.exists()) {
				// 检测文件是否允许删除，如果不允许删除，将会抛出SecurityException
				// SecurityManager securityManager = new SecurityManager();
				// securityManager.checkDelete(zipFilePath);
				// 删除已存在的目标文件
				zipFile.delete();
			}

			cos = new CheckedOutputStream(new FileOutputStream(zipFile), new CRC32());
			zos = new ZipOutputStream(cos);

			// 如果只是压缩一个文件，则需要截取该文件的父目录
			String srcRootDir = dataHand(srcPath, srcFile);
			// 调用递归压缩方法进行目录或文件压缩
			zip(srcRootDir, srcFile, zos);
			zos.flush();
		} catch (Exception e) {
			logger.error("压缩文件出错：{}", e.getMessage());
			throw new RuntimeException("压缩文件出错：{}", e);
		} finally {
			try {
				if (zos != null) {
					zos.close();
				}
			} catch (Exception e) {
				logger.error("压缩文件出错：{}", e.getMessage());
				throw new RuntimeException("压缩文件出错：{}", e);
			}
		}
		logger.info("压缩文件完成");
	}

	private static String dataHand(String srcPath, File srcFile) {
		String srcRootDir = srcPath;
		if (srcFile.isFile()) {
			int index = srcPath.lastIndexOf(File.separator);
			if (index != -1) {
				srcRootDir = srcPath.substring(0, index);
			}
		}
		return srcRootDir;
	}

	public static void fileCopy(String sourceFile, String destFile) {
		int byteread = 0;
		File oldfile = new File(sourceFile);
		try {
			if (oldfile.exists()) { // 文件存在时
				createDir(destFile);
				FileInputStream inStream = new FileInputStream(oldfile); // 读入原文件
				FileOutputStream fs = new FileOutputStream(new File(destFile, oldfile.getName()));
				byte[] buffer = new byte[BYTE_SIZE];
				while ((byteread = inStream.read(buffer)) != -1) {
					fs.write(buffer, 0, byteread);
				}
				inStream.close();
				fs.close();
			} else {
				logger.error("源文件不存在" + sourceFile);
				throw new RuntimeException("源文件不存在" + sourceFile);
			}
		} catch (Exception e) {
			logger.error("复制单个文件操作出错:{}", e.getMessage());
			throw new RuntimeException("复制文件出错", e);
		}
	}

	public static void copy(String src, String des) {
		File file1 = new File(src);
		File[] fs = file1.listFiles();
		File file2 = new File(des);
		if (!file2.exists()) {
			file2.mkdirs();
		}
		for (File f : fs) {
			if (f.isFile()) {
				fileCopy(f.getPath(), des + File.separator); // 调用文件拷贝的方法
				// fileCopy(f.getPath(), des + File.separator + f.getName()); //
				// 调用文件拷贝的方法
			} else if (f.isDirectory()) {
				copy(f.getPath(), des + File.separator + f.getName());
			}
		}

	}

	public static String stringFilter(String str) {
		// 只允许字母和数字
		String regEx = "[^a-zA-Z0-9]";
		// 清除掉所有特殊字符
		// String
		// regEx="[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]";
		Pattern p = Pattern.compile(regEx);
		Matcher m = p.matcher(str);
		return m.replaceAll("").trim();
	}

	public static void updateJar(String jarPath, String filePath) {

		String command;
		command = "jar uf " + jarPath + " " + filePath;
		try {
			Process process = Runtime.getRuntime().exec(command);
			printStream(process.getInputStream());
			printStream(process.getErrorStream());
		} catch (IOException e) {
			logger.error("更新jar包失败：{}", e.getMessage());
			throw new RuntimeException("更新jar包失败", e);
		}
	}

	public static void printStream(InputStream inputStream) {
		if (inputStream == null) {
			logger.warn("输入为空");
			return;
		}

		String line = "";
		try (BufferedReader input = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {
			while ((line = input.readLine()) != null) {
				logger.info(line);
			}
		} catch (IOException e1) {
			logger.error("输出流失败" + e1);
		}
	}

}
